/*
 * @features: 功能
 * @description: 说明
 * @Date: 2021-09-15 23:50:56
 * @Author: judu233(769471424@qq.com)
 * @LastEditTime: 2021-09-15 23:50:57
 * @LastEditors: judu233
 */
import * as is from './is';
import * as util from './util';

const defaults = util.defaults({
    root: null,
    weight: edge => 1,
    directed: false,
    alpha: 0
});

let elesfn = ({
    degreeCentralityNormalized: function (options) {
        options = defaults(options);

        let cy = this.cy();
        let nodes = this.nodes();
        let numNodes = nodes.length;

        if (!options.directed) {
            let degrees = {};
            let maxDegree = 0;

            for (let i = 0; i < numNodes; i++) {
                let node = nodes[i];

                // add current node to the current options object and call degreeCentrality
                options.root = node;

                let currDegree = this.degreeCentrality(options);

                if (maxDegree < currDegree.degree) {
                    maxDegree = currDegree.degree;
                }

                degrees[node.id()] = currDegree.degree;
            }

            return {
                degree: function (node) {
                    if (maxDegree === 0) { return 0; }

                    if (is.string(node)) {
                        // from is a selector string
                        node = cy.filter(node);
                    }

                    return degrees[node.id()] / maxDegree;
                }
            };
        } else {
            let indegrees = {};
            let outdegrees = {};
            let maxIndegree = 0;
            let maxOutdegree = 0;

            for (let i = 0; i < numNodes; i++) {
                let node = nodes[i];
                let id = node.id();

                // add current node to the current options object and call degreeCentrality
                options.root = node;

                let currDegree = this.degreeCentrality(options);

                if (maxIndegree < currDegree.indegree)
                    maxIndegree = currDegree.indegree;

                if (maxOutdegree < currDegree.outdegree)
                    maxOutdegree = currDegree.outdegree;

                indegrees[id] = currDegree.indegree;
                outdegrees[id] = currDegree.outdegree;
            }

            return {
                indegree: function (node) {
                    if (maxIndegree == 0) { return 0; }

                    if (is.string(node)) {
                        // from is a selector string
                        node = cy.filter(node);
                    }

                    return indegrees[node.id()] / maxIndegree;
                },
                outdegree: function (node) {
                    if (maxOutdegree === 0) { return 0; }

                    if (is.string(node)) {
                        // from is a selector string
                        node = cy.filter(node);
                    }

                    return outdegrees[node.id()] / maxOutdegree;
                }

            };
        }

    }, // degreeCentralityNormalized

    // Implemented from the algorithm in Opsahl's paper
    // "Node centrality in weighted networks: Generalizing degree and shortest paths"
    // check the heading 2 "Degree"
    degreeCentrality: function (options) {
        options = defaults(options);

        let cy = this.cy();
        let callingEles = this;
        let { root, weight, directed, alpha } = options;

        root = cy.collection(root)[0];

        if (!directed) {
            let connEdges = root.connectedEdges().intersection(callingEles);
            let k = connEdges.length;
            let s = 0;

            // Now, sum edge weights
            for (let i = 0; i < connEdges.length; i++) {
                s += weight(connEdges[i]);
            }

            return {
                degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
            };
        } else {
            let edges = root.connectedEdges();
            let incoming = edges.filter(edge => edge.target().same(root) && callingEles.has(edge));
            let outgoing = edges.filter(edge => edge.source().same(root) && callingEles.has(edge));
            let k_in = incoming.length;
            let k_out = outgoing.length;
            let s_in = 0;
            let s_out = 0;

            // Now, sum incoming edge weights
            for (let i = 0; i < incoming.length; i++) {
                s_in += weight(incoming[i]);
            }

            // Now, sum outgoing edge weights
            for (let i = 0; i < outgoing.length; i++) {
                s_out += weight(outgoing[i]);
            }

            return {
                indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
                outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
            };
        }
    } // degreeCentrality

}); // elesfn

// nice, short mathemathical alias
elesfn[`dc`] = elesfn.degreeCentrality;
elesfn[`dcn`] = elesfn[`degreeCentralityNormalised`] = elesfn.degreeCentralityNormalized;

export default elesfn;
